/*
 * Albareth - an action roleplaying game.
 * Copyright (C) 2008 Jens Anuth
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *  02110-1301, USA.
 * 
 * $Id: AbstractSlickGameEffect.java 46 2008-05-15 18:09:39Z jens464 $
*/
package albareth.frontend.slick;

import java.util.List;

import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;

import com.sun.corba.se.connection.GetEndPointInfoAgainException;

import albareth.core.GameEffect;
import albareth.core.entity.Entity;
import albareth.core.entity.Item;
import albareth.core.entity.Mobile;
import albareth.util.Location;

public abstract class AbstractSlickGameEffect
{
    protected GameEffect config = null;
    protected float x = 0;
    protected float y = 0;
    private int _age = 0;
    private boolean _active = true;

    protected Vector2f currMapVec;
    protected Vector2f targMapVec;
    protected Vector2f motion;


    public AbstractSlickGameEffect(GameEffect effectConfig)
    {
        config = effectConfig;
    }
    
    public final void render(Graphics g, int screenX, int screenY) throws SlickException
    {
        renderEffect(g, screenX + (int)x, screenY + (int)y);
    }

    public void update(int delta) throws SlickException
    {
        updateEffect(delta);
        
        Entity entity = config.getEntity();
        if (entity != null && entity.isMobile())
        {
            //TODO: also remove effect when no longer on the drawn map.
            if (((Mobile)entity).isFlagSet(Mobile.FLAG_DEAD)) {
                deactivate();
            }
        }
        
        if (hasFinished())
        {
            deactivate();
            
        } else if (config.getDuration() > 0) {
            _age += delta;
            if (_age > config.getDuration()) {
                deactivate();
            }
            
        } else if (hitSomething())  {
            deactivate();

        } else {
            if (config.isSpinning()) {
                spin(delta);
            }
        }
        

            
    }
    
    public void deactivate()
    {
        _active = false;
        config.notifyObserver();
    }
    
    public final boolean isActive()
    {
        return _active;
    }

    public boolean hitSomething()
    {
        if (config.getSpeed() == 0) {
            return false;
        }

        Location currLoc = config.getCurrentLocation();
        
        if (currLoc.getGroundType().isWall()) {
            return true;
        }
        
        //flight destination reached?
        if (!config.isHitFlagSet(GameEffect.FLY_INFINITE) &&
            currMapVec.distance(targMapVec) < 6)
        {
            return true;
        }

        //hit a mobile?
        Mobile mob = currLoc.getMobile();
        if (mob != null)
        {
            if (config.isHitFlagSet(GameEffect.HIT_ENEMIES_ON_PATH) &&
                config.getEmitter().isMobile() &&
                ((Mobile)config.getEmitter()).isHostileTo(mob))
            {
                return true;
            }
            return false; //if there is a mob, there cannot be a blocking
                          //item too. so nothing to do anymore
        }
        
        //hit an item?
        if (config.isHitFlagSet(GameEffect.HIT_ITEMS_ON_PATH))
        {
            List items = currLoc.getItems();
            if (items != null) {
                for (int i = 0; i < items.size(); i++) {
                    if (((Item)items.get(i)).isBlocking()) {
                        return true;
                    }
                }
            }
        }
        
        return false;
    }


    /**
     * Calculate the motion vector and the angular rotation.
     */
    public void updateMotion()
    {
        motion = new Vector2f(
                targMapVec.x - currMapVec.x,
                targMapVec.y - currMapVec.y
        );
        motion.normalise();
        motion.scale(config.getSpeed());

        //calculate new rotation vector:
        double cos = motion.x / Math.sqrt(motion.x*motion.x + motion.y*motion.y);
        double angle = Math.acos(cos);
        if (motion.y < 0) { //angle = 360 - angle;
            angle = Math.PI*2 - angle;
        }
        angle = (angle + Math.PI/2) % (Math.PI*2);
        setRotation((float)Math.toDegrees(angle + Math.PI));
    }
    

    /**
     * Spin the effect graphics around.
     */
    public void spin(int delta)
    {
        setRotation(
                (getRotation() + delta) % 360
        );
    }

    public void setX(float x)
    {
        this.x = x;
    }

    public void setY(float y)
    {
        this.y = y;
    }

    protected abstract void setRotation(float degrees);
    
    protected abstract float getRotation();

    protected abstract boolean hasFinished();

    protected abstract void updateEffect(int delta) throws SlickException;
    
    protected abstract void renderEffect(Graphics g, int x, int y) throws SlickException;




}
